package org.zoomdev.zoom.protobuf.modules;

import io.protostuff.ByteArrayInput;
import io.protostuff.ProtostuffOutput;
import io.protostuff.runtime.EnumIO;
import io.protostuff.runtime.RuntimeEnv;
import org.zoomdev.zoom.common.exceptions.ZoomException;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;

public final class BasicSchema {
    static final ISchema BOOL = new BasicSchema.BooleanSchema();
    static final ISchema INT = new BasicSchema.IntegerSchema();
    static final ISchema FLOAT = new BasicSchema.FloatSchema();

    static final ISchema DOUBLE = new BasicSchema.DoubleSchema();

    static final ISchema SHORT = new BasicSchema.ShortSchema();

    static final ISchema BYTE = new BasicSchema.ByteSchema();
    static final ISchema LONG = new BasicSchema.LongSchema();
    static final ISchema STRING = new BasicSchema.StringSchema();
    static final ISchema BYTES = new BytesSchema();
    static final ISchema DATE = new DateSchema();


    public static class DoubleSchema implements IBasicSchema {

        public static final Double ZERO = (double) 0;

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeDouble(index, (Double) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readDouble();
        }

        @Override
        public Object getDefaultValue() {
            return ZERO;
        }
    }

    public static class FloatSchema implements IBasicSchema {

        public static final Float ZERO = 0f;

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeFloat(index, (Float) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readFloat();
        }

        @Override
        public Object getDefaultValue() {
            return ZERO;
        }
    }

    public static class IntegerSchema implements IBasicSchema {

        public static final Integer ZERO = 0;

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeInt32(index, (Integer) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readInt32();
        }

        @Override
        public Object getDefaultValue() {
            return ZERO;
        }
    }

    public static class ByteSchema implements IBasicSchema {

        public static final byte ZERO = 0;

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeInt32(index, (Byte) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return (byte) input.readInt32();
        }

        @Override
        public Object getDefaultValue() {
            return ZERO;
        }
    }

    public static class ShortSchema implements IBasicSchema {

        public static final Short ZERO = 0;

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeInt32(index, (Short) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return (short) input.readInt32();
        }

        @Override
        public Object getDefaultValue() {
            return ZERO;
        }
    }

    public static class BooleanSchema implements IBasicSchema {
        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeBool(index, (Boolean) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readBool();
        }

        @Override
        public Object getDefaultValue() {
            return Boolean.FALSE;
        }
    }


    public static class BytesSchema implements ISchema {
        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeByteArray(index, (byte[]) value, false);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readByteArray();
        }
    }


    public static class DateSchema implements ISchema {


        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            Date date = (Date) value;
            output.writeInt64(index, date.getTime(), repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            long time = input.readInt64();
            return new Date(time);
        }
    }

    public static class EmuSchema implements ISchema {

        private final EnumIO<? extends Enum<?>> eio;

        public EmuSchema(Class<?> type) {
            try {
                Method method = RuntimeEnv.ID_STRATEGY.getClass().getDeclaredMethod("getEnumIO", Class.class);
                method.setAccessible(true);
                this.eio = (EnumIO<? extends Enum<?>>) method.invoke(RuntimeEnv.ID_STRATEGY, type);
            } catch (Exception e) {
                throw new ZoomException(e);
            }
        }

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            eio.writeTo(output, index, repeat, (Enum<?>) value);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return eio.readFrom(input);
        }
    }

    public static class LongSchema implements ISchema {
        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeInt64(index, (Long) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readInt64();
        }
    }

    static class StringSchema implements ISchema {
        private StringSchema() {

        }

        @Override
        public void write(ProtostuffOutput output, int index, Object value, boolean repeat) throws IOException {
            output.writeString(index, (String) value, repeat);
        }

        @Override
        public Object mergeFrom(ByteArrayInput input) throws IOException {
            return input.readString();
        }
    }
}
